/*
  Передача лидера по выходному каналу
*/
inline double transmissionLeaderCS(double time, int channel) {
  if (time < networkSettings.timeOfReleaseChannels[channel]) { // если выходной
                                                               // канал занят
    time = networkSettings.timeOfReleaseChannels[channel]; // дождаться освобождения
  }
  return time + networkSettings.timeChannelSwitching;
}

/*
  Поиск лидера между двумя претендентами
*/
inline void leaderBetweenTwoCS(double* time1, int* flow1, double time2, int flow2) {
  if (*time1 > time2) {
    *time1 = time2;
    *flow1 = flow2;
  }
}

/*
  Поиск лидера в узле [3]
*/
void leaderForHub3CS(int* flowLeader) {
  *flowLeader = 0;
  networkSettings.currentTime = networkSettings.timeMessages[0];

  double time = -1.0;
  // узел [0]: входной поток [1], выходной канал [0]
  time = transmissionLeaderCS(networkSettings.timeMessages[1], 0);
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, 1);

  // узел [1]: входной поток [2], выходной канал [1]
  time = transmissionLeaderCS(networkSettings.timeMessages[2], 1);
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, 2);

  // узел [2]: входной поток [3], выходной канал [2]
  time = transmissionLeaderCS(networkSettings.timeMessages[3], 2);
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, 3);
}

/*
  Поиск лидера в узле [4]
*/
void leaderForHub4CS(int* flowLeader) {
  networkSettings.currentTime = transmissionLeaderCS(networkSettings.currentTime, 3);

  for (int i = 4; i <= 7; ++i) {
    leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader,
                     networkSettings.timeMessages[i], i);
  }
}

/*
  Поиск лидера в узле [6]
*/
void leaderForHub6CS(int* flowLeader) {
  networkSettings.currentTime = transmissionLeaderCS(networkSettings.currentTime, 4);

  double time = -1.0;
  // поиск лидера в узле [5]
  int flowLeaderTemp = networkSettings.timeMessages[8] >
                       networkSettings.timeMessages[9] ? 9 : 8;
  time = transmissionLeaderCS(networkSettings.timeMessages[flowLeaderTemp], 5);

  // поиск лидера в узле [6]
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, flowLeaderTemp);

  // поиск лидера в узле [7]
  flowLeaderTemp = networkSettings.timeMessages[10] >
                   networkSettings.timeMessages[11] ? 11: 10;
  time = transmissionLeaderCS(networkSettings.timeMessages[flowLeaderTemp], 6);

  // повторный поиск лидера в узле [6]
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, flowLeaderTemp);
}

/*
  Поиск лидера в узле [9]
*/
void leaderForHub9CS(int* flowLeader) {
  networkSettings.currentTime = transmissionLeaderCS(networkSettings.currentTime, 7);

  double time = -1.0;
  // узел [8]: входной поток [12], выходной канал [8]
  time = transmissionLeaderCS(networkSettings.timeMessages[12], 8);
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, 12);

  // узел [10]: входной поток [13], выходной канал [9]
  time = transmissionLeaderCS(networkSettings.timeMessages[13], 9);
  leaderBetweenTwoCS(&networkSettings.currentTime, flowLeader, time, 13);
}

/*
  Поиск лидера в узле [11]
*/
inline void leaderForHub11CS() {
  networkSettings.currentTime = transmissionLeaderCS(networkSettings.currentTime, 10);
}

/*
  Освобождение занятых каналов
*/
void clearingOfChannelsCS(const int& flowLeader) {
  switch (flowLeader) {
  case 0: // channels: ->[3]->[4]->[7]->[10]->
    networkSettings.timeOfReleaseChannels[3] =
        networkSettings.timeOfReleaseChannels[4] =
        networkSettings.timeOfReleaseChannels[7] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 1: // channels: ->[0]->[3]->[4]->[7]->
    networkSettings.timeOfReleaseChannels[0] =
        networkSettings.timeOfReleaseChannels[3] =
        networkSettings.timeOfReleaseChannels[4] =
        networkSettings.timeOfReleaseChannels[7] = networkSettings.currentTime;
    break;
  case 2: // channels: ->[1]->[3]->[4]->[7]->
    networkSettings.timeOfReleaseChannels[1] =
        networkSettings.timeOfReleaseChannels[3] =
        networkSettings.timeOfReleaseChannels[4] =
        networkSettings.timeOfReleaseChannels[7] = networkSettings.currentTime;
    break;
  case 3: // channels: ->[2]->[3]->[4]->[7]->[10]->
    networkSettings.timeOfReleaseChannels[2] =
        networkSettings.timeOfReleaseChannels[3] =
        networkSettings.timeOfReleaseChannels[4] =
        networkSettings.timeOfReleaseChannels[7] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 4:  case 5:  case 6:  case 7: // channels: ->[4]->[7]->[10]->
    networkSettings.timeOfReleaseChannels[4] =
        networkSettings.timeOfReleaseChannels[7] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 8:  case 9: // channels: ->[5]->[7]->[10]->
    networkSettings.timeOfReleaseChannels[5] =
        networkSettings.timeOfReleaseChannels[7] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 10: case 11: // channels: ->[6]->[7]->[10]->
    networkSettings.timeOfReleaseChannels[6] =
        networkSettings.timeOfReleaseChannels[7] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 12: // channels: ->[8]->[10]->
    networkSettings.timeOfReleaseChannels[8] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  case 13: // channels: ->[9]->[10]->
    networkSettings.timeOfReleaseChannels[9] =
        networkSettings.timeOfReleaseChannels[10] = networkSettings.currentTime;
    break;
  default:
    qFatal("error in clearingOfChannels(): лидер неизвестного потока.");
  }
}

/*
  Поиск общего лидера
*/
void totalLeaderCS(int* flowLeader) {
  leaderForHub3CS(flowLeader);
  leaderForHub4CS(flowLeader);
  leaderForHub6CS(flowLeader);
  leaderForHub9CS(flowLeader);

  // для потоков [1] и [2] узлом назначения является hub [9], для остальных [11]
  if (*flowLeader != 1 && *flowLeader != 2) {
    leaderForHub11CS();
  }

  if (*flowLeader < 0 || *flowLeader >= numberOfFlows) {
    qFatal("error in totalLeader(): лидер неизвестного потока.");
  }
}

/*
  Расчет задержки коммутации каналов
*/
double channelSwitchingDelay()
{
  double totalTime        = 0.0; // общее время работы сети
  double totalSize        = 0.0; // общий размер переданных данных
  int    numberOfMessages = 0;   // число переданных контролируемым потоком сообщений
  int    flowLeader       = -1;  // номер потока занявшего сеть

  while (numberOfMessages < inputData.limitMessages) {
    totalLeaderCS(&flowLeader); // поиск лидера

    networkSettings.currentTime += networkSettings.timeChannelSwitching +
        networkSettings.sizeOfMessages[flowLeader] / inputData.capacity;

    while (numberOfMessages < inputData.limitMessages) {
      if (flowLeader == inputData.controlledFlow) { // если лидер из
                                                    // контролируемого потока
        totalSize += networkSettings.sizeOfMessages[inputData.controlledFlow];
        totalTime += networkSettings.currentTime -
            networkSettings.timeMessages[inputData.controlledFlow];
        ++numberOfMessages;

        if (numberOfMessages == inputData.limitMessages / 2) { // вывести задержку
                                                               // на половине пути
          cout << "(половина) задержка коммутации каналов:\t\t"
               << totalTime / numberOfMessages << endl;
          cout << "(половина) средний размер сообщений:\t\t"
               << totalSize / numberOfMessages << endl;
        }
      }

      // формирование нового сообщения
      networkSettings.timeMessages[flowLeader]  += conversion(inputData.intensity);
      networkSettings.sizeOfMessages[flowLeader] = conversion(inputData.messageLength);

      // сформированное сообщение успевает передаться ?
      if (networkSettings.timeMessages[flowLeader] <= networkSettings.currentTime) {
        networkSettings.currentTime += networkSettings.sizeOfMessages[flowLeader] /
            inputData.capacity;
      } else {
        clearingOfChannelsCS(flowLeader);
        break; // вернуться к началу алгоритма
      }
    }
  }

  cout << "\nзадержка коммутации каналов:\t\t\t" << totalTime / numberOfMessages << endl;
  cout << "средний размер сообщений:\t\t\t" << totalSize / numberOfMessages << endl << endl;

  return totalTime / numberOfMessages;
}
